package com.example.user.service.permission;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.example.user.dal.dataobject.permission.MenuDO;
import com.example.user.dal.dataobject.permission.RoleDO;
import com.example.user.dal.dataobject.permission.RoleMenuDO;
import com.example.user.dal.dataobject.permission.UserRoleDO;
import com.example.user.dal.mysql.permission.RoleMenuMapper;
import com.example.user.dal.mysql.permission.UserRoleMapper;
import com.example.user.dal.redis.RedisKeyConstants;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class PermissionServiceImpl implements PermissionService {

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RoleService roleService;

    @Resource
    private RoleMenuMapper roleMenuMapper;

    @Resource
    private MenuService menuService;

    @Override
    @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId")
    public void processMenuDeleted(Long menuId) {
        roleMenuMapper.delete(Wrappers.<RoleMenuDO>lambdaQuery()
                .eq(RoleMenuDO::getMenuId, menuId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST,
                    allEntries = true), // allEntries 清空所有缓存，此处无法方便获得 roleId 对应的 menu 缓存们
            @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST,
                    allEntries = true) // allEntries 清空所有缓存，此处无法方便获得 roleId 对应的 user 缓存们
    })
    public void processRoleDeleted(Long roleId) {
        // 标记删除 UserRole
        userRoleMapper.delete(Wrappers.<UserRoleDO>lambdaQuery()
                .eq(UserRoleDO::getRoleId, roleId));
        // 标记删除 RoleMenu
        roleMenuMapper.delete(Wrappers.<RoleMenuDO>lambdaQuery()
                .eq(RoleMenuDO::getRoleId, roleId));
    }

    @Override
    public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return Collections.emptySet();
        }
        List<RoleMenuDO> roleMenuDOS = roleMenuMapper.selectList(Wrappers.<RoleMenuDO>lambdaQuery()
                .in(RoleMenuDO::getRoleId, roleIds));
        return roleMenuDOS.stream().map(RoleMenuDO::getMenuId).collect(Collectors.toSet());
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST,
            allEntries = true) // allEntries 清空所有缓存，主要一次更新涉及到的 menuIds 较多，反倒批量会更快
    public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
        // 计算新增和删除的菜单编号
        Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);

        // 只保留存在的菜单
        List<Long> existedMenuIds = menuService.list().stream().map(MenuDO::getId).toList();
        menuIdList.retainAll(existedMenuIds);

        // 获得角色拥有菜单编号
        Set<Long> dbMenuIds = getRoleMenuListByRoleId(roleId);
        Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);

        // 已有的 - 待新增的 = 剩下要删除的（正常情况不会出现，这样可以清理脏数据）
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
        // 执行新增和删除。对于已经授权的菜单，不用做任何处理
        if (CollUtil.isNotEmpty(createMenuIds)) {
            List<RoleMenuDO> roleMenuDOS = createMenuIds.stream().map(menuId -> {
                RoleMenuDO entity = new RoleMenuDO();
                entity.setRoleId(roleId);
                entity.setMenuId(menuId);
                return entity;
            }).toList();
            Db.saveBatch(roleMenuDOS);
        }
        if (CollUtil.isNotEmpty(deleteMenuIds)) {
            roleMenuMapper.delete(Wrappers.<RoleMenuDO>lambdaQuery()
                    .eq(RoleMenuDO::getRoleId, roleId)
                    .in(RoleMenuDO::getMenuId, deleteMenuIds));
        }
    }

    @Override
    @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId")
    public void assignUserRole(Long userId, Set<Long> roleIds) {
        // 计算新增和删除的角色编号
        Set<Long> roleIdList = CollUtil.emptyIfNull(roleIds);

        // 只保留存在的角色
        List<Long> existedRoleIds = roleService.list().stream().map(RoleDO::getId).toList();
        roleIdList.retainAll(existedRoleIds);

        // 获得角色拥有角色编号
        Set<Long> dbRoleIds = userRoleMapper.selectList(Wrappers.<UserRoleDO>lambdaQuery()
                        .in(UserRoleDO::getUserId, Collections.singletonList(userId)))
                .stream().map(UserRoleDO::getRoleId).collect(Collectors.toSet());
        Collection<Long> createRoleIds = CollUtil.subtract(roleIdList, dbRoleIds);

        // 已有的 - 待新增的 = 剩下要删除的（正常情况不会出现，这样可以清理脏数据）
        Collection<Long> deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIdList);
        // 执行新增和删除。对于已经授权的角色，不用做任何处理
        if (!CollectionUtil.isEmpty(createRoleIds)) {
            List<UserRoleDO> userRoleDOS = createRoleIds.stream().map(roleId -> {
                UserRoleDO entity = new UserRoleDO();
                entity.setUserId(userId);
                entity.setRoleId(roleId);
                return entity;
            }).toList();
            Db.saveBatch(userRoleDOS);
        }
        if (!CollectionUtil.isEmpty(deleteMenuIds)) {
            userRoleMapper.delete(Wrappers.<UserRoleDO>lambdaQuery()
                    .eq(UserRoleDO::getUserId, userId)
                    .in(UserRoleDO::getRoleId, deleteMenuIds));
        }
    }
}
